#include "libcomm.h"
#if (COMM_FEATURE_LIBEVENT==1)

#ifdef COMM_MSVC
#pragma comment(lib,"Ws2_32.lib")
#ifdef COMM_X64
#pragma comment(lib,"../../depend/libevent/bin/x64/libevent_core.lib")
#pragma comment(lib,"../../depend/libevent/bin/x64/libevent_extras.lib")
#if (COMM_FEATURE_SSL==1)
	#pragma comment(lib,"../../depend/libevent/bin/x64/libevent_openssl.lib")
	#pragma comment(lib,"../../depend/openssl/bin/x64/libeay32MD.lib")
	#pragma comment(lib,"../../depend/openssl/bin/x64/ssleay32MD.lib")
#endif
#else
#pragma comment(lib,"../../depend/libevent/bin/win32/libevent_core.lib")
#pragma comment(lib,"../../depend/libevent/bin/win32/libevent_extras.lib")
#if (COMM_FEATURE_SSL==1)
	#pragma comment(lib,"../../depend/libevent/bin/win32/libevent_openssl.lib")
	#pragma comment(lib,"../../depend/openssl/bin/win32/libeay32MD.lib")
	#pragma comment(lib,"../../depend/openssl/bin/win32/ssleay32MD.lib")
#endif
#endif
#endif
#if (COMM_FEATURE_SSL==1)
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include <event2/bufferevent_ssl.h>
#endif
#include <event2/event.h>
#include <event2/bufferevent.h>
#include <event2/listener.h>
#include <event2/buffer.h>
#include <event2/util.h>
#include <event2/thread.h>
#define LOGFUNCERROR_EVENT(func)	\
	LOGERR("%s:%d: %s: Function \'%s\' error: (%d) %s", __FILE__,  __LINE__, __FUNCTION__, # func, EVUTIL_SOCKET_ERROR(),evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()))


////////////////////////////////////////////////////////////////////////// SocketAddress

SocketAddress::SocketAddress(int port, const char* ip):m_family(AF_INET)
{
	memset(&m_addr.addr_in, 0, sizeof(m_addr.addr_in));
	m_addr.addr_in.sin_family = AF_INET;
	if (NULL == ip)
		m_addr.addr_in.sin_addr.s_addr = htonl(INADDR_ANY);
	else
		m_addr.addr_in.sin_addr.s_addr = inet_addr(ip);
	m_addr.addr_in.sin_port = htons(port);
}

#ifdef __GNUC__
SocketAddress::SocketAddress(const char* unix_path):m_family(AF_UNIX)
{
	memset(&m_addr.addr_un, 0, sizeof(m_addr.addr_un));
	m_addr.addr_un.sun_family = AF_UNIX;
	strncpy(m_addr.addr_un.sun_path,unix_path,sizeof(m_addr.addr_un.sun_path)-1);
}
#endif

SocketAddress::SocketAddress(sockaddr *sa, int socklen)
{
	m_family = sa->sa_family;
	if(AF_INET == m_family)
	{
		memcpy(&m_addr.addr_in,sa,socklen);
	}
#ifdef __GNUC__
	else if(AF_UNIX == m_family)
	{
		memcpy(&m_addr.addr_un,sa,socklen);
	}
#endif
}

SocketAddress::operator sockaddr*() const
{ 
	if(AF_INET == m_family)
		return (sockaddr*)&m_addr.addr_in;
#ifdef __GNUC__
	else if(AF_UNIX == m_family)
		return (sockaddr*)&m_addr.addr_un;
#endif
	return NULL;
}

int SocketAddress::Size() const 
{ 
	if(AF_INET == m_family)
		return sizeof(m_addr.addr_in);
#ifdef __GNUC__
	else if(AF_UNIX == m_family)
		return sizeof(m_addr.addr_un);
#endif
	return 0;
}

String SocketAddress::ToString(bool include_port) const
{
	if(AF_INET == m_family)
	{
		if (!include_port)
			return String::format("%s", inet_ntoa(m_addr.addr_in.sin_addr));
		return String::format("%s:%d", inet_ntoa(m_addr.addr_in.sin_addr), ntohs(m_addr.addr_in.sin_port));
	}
#ifdef __GNUC__
	else if(AF_UNIX == m_family)
		return m_addr.addr_un.sun_path;
#endif
	return "";
}

int SocketAddress::Port() const
{
	if(AF_INET == m_family)
		return ntohs(m_addr.addr_in.sin_port);
	return 0;
}

int SocketAddress::Family() const
{
	return m_family;
}

////////////////////////////////////////////////////////////////////////// SocketReactor

// It is not safe to invoke Libevent functions from within a user-provided event_log_cb callback!
//
static void libevent_log_cb(int severity, const char *msg)
{
	const char *s;
	switch (severity) {
		case EVENT_LOG_DEBUG: s = "debug";	break;
		case EVENT_LOG_MSG:   s = "msg  ";  break;
		case EVENT_LOG_WARN:  s = "warn ";  break;
		case EVENT_LOG_ERR:   s = "error";	break;
		default:              s = "?";		break; /* never reached */
	}
	LOGWARN("LIBEVENT %s: %s",s,msg);
}

static void libevent_fatal_cb(int err)
{
	LOGWARN("LIBEVENT detects a non-recoverable internal error: %d.",err);
	::exit(EXIT_FAILURE);
}

class SocketReactor : private Thread
{
public:
	struct event_base* evbase;
	SocketReactor():Thread("socket_dispatch_trd"),evbase(NULL)
	{
#ifdef COMM_MSVC
		WSADATA wsa_data;
		MYLIB_ASSERT(0==WSAStartup(0x0202, &wsa_data));
#endif
		event_set_log_callback(libevent_log_cb);
		event_set_fatal_callback(libevent_fatal_cb);
		//event_enable_debug_mode();

#ifdef COMM_MSVC
		MYLIB_ASSERT(0==evthread_use_windows_threads());
#else
		MYLIB_ASSERT(0==evthread_use_pthreads());
#endif

		evbase = event_base_new();//MT-SAFE
		MYLIB_ASSERT_NOTNULL(evbase);
		LOGINFO("libevent_%s with backend method: %s.",event_get_version(),event_base_get_method(evbase));
	}

	~SocketReactor()
	{
		if(NULL != evbase)
		{
			event_base_free(evbase);
			evbase = NULL;
		}
#ifdef COMM_MSVC
		WSACleanup();
#endif
	}

	void Start()
	{
		MYLIB_ASSERT_NOTNULL(evbase);
		this->Run();
	}

	void Stop(bool delay=false)
	{
		MYLIB_ASSERT_NOTNULL(evbase);
		//event_base_dump_events(evbase,stdout);
		struct timeval timeout = {0,100000};
		// waiting for trigger SocketClient::OnClose() invoking after SocketClient::Close().
		MYLIB_ASSERT(0==event_base_loopexit(evbase,delay?(&timeout):NULL)); 
		this->Join();
	}

	virtual int Process()
	{
		MYLIB_ASSERT_NOTNULL(evbase);
		int err = event_base_dispatch(evbase);
#if 0
		switch(err)
		{
		case 0:
			{
				LOGNOTICE("libevent exited normally!");
				break;
			}
		case 1:
			{
				LOGNOTICE("libevent exited because there were no more pending or active events!");
				break;
			}
		default:
			{
				LOGNOTICE("libevent exited because of some unhandled error(%d) in the backend!",err);
				break;
			}
		}
#endif
		return err;
	}
};


////////////////////////////////////////////////////////////////////////// SocketClient

static void __close_event_cb(evutil_socket_t fd, short what, void *arg);
static void __conn_readcb(struct bufferevent *bev, void* ctx);
static void __conn_eventcb(struct bufferevent *bev, short events, void* ctx);
static void __accept_conn_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *sa, int socklen, void* ctx);
static void __accept_error_cb(struct evconnlistener *listener, void* ctx);

#define CLIENTIMP(inst)	((SocketClientImp*)inst->m_inst_opaque)
class SocketClientImp
{
public:
	ThreadLock m_lock;
	SocketClient* client;
	struct event* evclose;
	struct bufferevent* bev;
	SocketAddress address;
	SocketClient::ConnectionState state;
	bool isConnector;
	SocketClientImp(SocketClient* handler)
		:client(handler),evclose(NULL),bev(NULL),state(SocketClient::CONN_INIT),isConnector(false){}
	~SocketClientImp()
	{
		MYLIB_ASSERT_NOTNULL(client);
		LOGNOTICE("Connection \"%s\" destroyed.",(char*)address.ToString());
		if(NULL != bev)
		{ 
			bufferevent_free(bev);
			bev = NULL;
		}
		if(NULL != evclose)
		{
			event_free(evclose);
			evclose = NULL;
		}
	}

	SocketClient::ConnectionState GetState()
	{
		ThreadLockGuard guard(&m_lock);
		return this->state;
	}

	bool Write(const void* buffer, int size)
	{
		ThreadLockGuard guard(&m_lock);
		if((int)this->state < SocketClient::CONN_EOF && 
			NULL != this->bev && NULL != buffer && size > 0)
		{
			struct evbuffer *output = bufferevent_get_output(this->bev);
			if(0 == evbuffer_add(output,buffer,size))
				return true;
			LOGFUNCERROR_EVENT(evbuffer_add);
		}
		LOGWARN("SocketClient::Write(%p,%d) failure.",buffer,size);
		return false;
	}

	void Close()
	{
		ThreadLockGuard guard(&m_lock);
		if((int)this->state < SocketClient::CONN_EOF)
		{
			LOGNOTICE("Closing Connection \"%s\" by manual...",(char*)client->Address().ToString());
			this->StateChanged(SocketClient::CONN_SHUTDOWN);
		}
	}

	void StateChanged(SocketClient::ConnectionState newState)
	{
		ThreadLockGuard guard(&m_lock);
		switch(newState)
		{
		case SocketClient::CONN_OPEN:
			{
				MYLIB_ASSERT(0 == bufferevent_enable(this->bev, EV_READ|EV_WRITE));
				client->OnOpen();
			}
			break;
		case SocketClient::CONN_EOF:
		case SocketClient::CONN_ERR:
		case SocketClient::CONN_SHUTDOWN:
			{
				if(NULL == this->evclose) // may be have triggered already.
				{
					struct event_base* base = bufferevent_get_base(this->bev);
					struct timeval timeout = {0,10};
					this->evclose = evtimer_new(base,__close_event_cb,this->client);
					MYLIB_ASSERT_NOTNULL(this->evclose);
					MYLIB_ASSERT(0 == evtimer_add(this->evclose, &timeout));
				}
			}
			break;
		case SocketClient::CONN_CLOSE:
			{
				// cancel close event.
				if(NULL != this->evclose)
				{
					MYLIB_ASSERT(0 == evtimer_del(this->evclose));
				}
				// do not trigger BEV_EVENT_XXX any more. event_base_dispatch() then return 1 in SocketConnector.
				if(0 != bufferevent_disable(this->bev,EV_READ|EV_WRITE))
					LOGFUNCERROR_EVENT(bufferevent_disable);
				// close peer gracefully. peer client would trigger CONN_EOF.
				evutil_socket_t sock = bufferevent_getfd(this->bev);
				if(0 != evutil_closesocket(sock))
					LOGFUNCERROR_EVENT(evutil_closesocket);
				client->OnClose(this->state);
			}
			break;
		default:
			break;
		}

		this->state = newState;
	}
};

SocketClient::SocketClient():m_inst_opaque(NULL),m_ref_count(1)
{ 
	m_inst_opaque = new SocketClientImp(this); 
	MYLIB_ASSERT_NOTNULL(m_inst_opaque);
}
SocketClient::~SocketClient()
{
	if(NULL != m_inst_opaque)
	{
		delete CLIENTIMP(this);
		m_inst_opaque = NULL;
	}
}
void SocketClient::Release()
{
	int refCount = --m_ref_count;
	if (0 == refCount)
		delete this;
}
void SocketClient::AddRef()
{
	++m_ref_count;
}

const SocketAddress& SocketClient::Address() const{ return CLIENTIMP(this)->address;}
SocketClient::ConnectionState SocketClient::State() const{ return CLIENTIMP(this)->GetState();}
bool SocketClient::AsClient() const{ return CLIENTIMP(this)->isConnector;}
bool SocketClient::Write(const void* buffer, int size){ return CLIENTIMP(this)->Write(buffer,size);}
void SocketClient::Close(){ CLIENTIMP(this)->Close();}


////////////////////////////////////////////////////////////////////////// SocketAcceptor

#if (COMM_FEATURE_SSL==1)
static void SSLError(const char *func)
{
	LOGERR("%s failed.", func);

	/* This is the OpenSSL function that prints the contents of the
	 * error stack to the specified file handle. */
	ERR_print_errors_fp (stderr);
}

static SSL_CTX* SSLInit(const char* crtfile=NULL, const char* keyfile=NULL)
{
	SSL_CTX* ctx;

	SSL_library_init();
	OpenSSL_add_all_algorithms();
	ERR_load_crypto_strings();
	SSL_load_error_strings();
	if (!RAND_poll())
	{
		SSLError("RAND_poll");
		return NULL;
	}

	if(NULL == keyfile)
		ctx = SSL_CTX_new(SSLv23_client_method());
	else
		ctx = SSL_CTX_new(SSLv23_server_method());
	if (NULL == ctx) 
	{
		SSLError("SSL_CTX_new");
		return NULL;
	}

	if(NULL != keyfile && NULL != crtfile)
	{
		if (!SSL_CTX_use_certificate_file(ctx, crtfile, SSL_FILETYPE_PEM) ||
			!SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM)) 
		{
			SSLError("SSL_CTX_use_PrivateKey_file");
			SSL_CTX_free(ctx);
			return NULL;
		}
	}

	SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
	return ctx;
}
#endif


class SocketAcceptorImp
{
public:
	struct evconnlistener* m_listener;
	SocketReactor m_reactor;
	SocketAddress m_listenAddress;
	SocketClientFactory* m_factory;
	AtomInt m_sockid;
#if (COMM_FEATURE_SSL==1)
	SSL_CTX* server_ctx;
#endif
	SocketAcceptorImp(const SocketAddress& listenAddress, SocketClientFactory* factory, const char* crtfile=NULL, const char* keyfile=NULL)
		:m_listener(NULL),m_listenAddress(listenAddress),m_factory(factory),m_sockid(1)
	{
		MYLIB_ASSERT_NOTNULL(m_factory);

#if (COMM_FEATURE_SSL==1)
		server_ctx = SSLInit(crtfile,keyfile);
		MYLIB_ASSERT_NOTNULL(server_ctx);
#else
		(void*)crtfile;
		(void*)keyfile;
#endif

		
#ifdef __GNUC__
		// do not mask write mode. then other user can to write unix socket file.
		File::WriteUnMasker umasker;
		if(AF_UNIX == listenAddress.Family())
			unlink(listenAddress.ToString());
#endif
		m_listener = evconnlistener_new_bind(m_reactor.evbase, __accept_conn_cb, this,
			LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE|LEV_OPT_CLOSE_ON_EXEC|LEV_OPT_THREADSAFE, -1,
			(sockaddr*)listenAddress,
			listenAddress.Size());

		if(NULL == m_listener)
		{
			LOGFUNCERROR_EVENT(evconnlistener_new_bind);
			return;
		}
		evconnlistener_set_error_cb(m_listener,__accept_error_cb);

		m_reactor.Start();
	}

	~SocketAcceptorImp()
	{
		m_reactor.Stop();

		if(NULL != m_listener)
		{
			evconnlistener_free(m_listener);
			m_listener = NULL;
		}

		if(NULL != m_factory)
		{
			m_factory->Release();
			m_factory = NULL;
		}
#if (COMM_FEATURE_SSL==1)
		if(NULL != server_ctx)
		{
			SSL_CTX_free(server_ctx);
			server_ctx = NULL;
		}
#endif
	}
};


#define SOCKETACCEPTOR ((SocketAcceptorImp*)m_inst)
SocketAcceptor::SocketAcceptor(const SocketAddress& listenAddress, SocketClientFactory* factory, const char* crtfile, const char* keyfile):m_inst(NULL)
{
	m_inst = new SocketAcceptorImp(listenAddress,factory,crtfile,keyfile);
	MYLIB_ASSERT_NOTNULL(m_inst);
}
SocketAcceptor::~SocketAcceptor()
{
	if(NULL != m_inst)
	{
		delete SOCKETACCEPTOR;
		m_inst = NULL;
	}
}
const SocketAddress& SocketAcceptor::Address() const
{
	return SOCKETACCEPTOR->m_listenAddress;
}

////////////////////////////////////////////////////////////////////////// SocketConnector

class SocketConnectorImp
{
public:
	SocketReactor m_reactor;
	SocketClient* m_client;
#if (COMM_FEATURE_SSL==1)
	SSL_CTX* server_ctx;
#endif
	SocketConnectorImp(const SocketAddress& serverAddress, SocketClient* client)
		:m_client(client)
	{
		struct bufferevent* bev = NULL;
		{
#if (COMM_FEATURE_SSL==1)
			server_ctx = SSLInit();
			MYLIB_ASSERT_NOTNULL(server_ctx);
			SSL* client_ctx = SSL_new(server_ctx);
			MYLIB_ASSERT_NOTNULL(client_ctx);
			bev = bufferevent_openssl_socket_new(m_reactor.evbase,-1,client_ctx,BUFFEREVENT_SSL_CONNECTING,
				BEV_OPT_CLOSE_ON_FREE|BEV_OPT_THREADSAFE);
#else
			bev = bufferevent_socket_new(m_reactor.evbase,-1,BEV_OPT_CLOSE_ON_FREE|BEV_OPT_THREADSAFE);
#endif
		}
		MYLIB_ASSERT_NOTNULL(bev);

		MYLIB_ASSERT_NOTNULL(m_client);
		CLIENTIMP(m_client)->address = serverAddress;
		CLIENTIMP(m_client)->bev = bev;
		CLIENTIMP(m_client)->isConnector = true;

		bufferevent_setcb(bev, __conn_readcb, NULL, __conn_eventcb, m_client);
		LOGNOTICE("Connecting to server \"%s\"...",(char*)serverAddress.ToString());
		if(bufferevent_socket_connect(bev,(sockaddr*)serverAddress,serverAddress.Size()))
		{
			LOGFUNCERROR_EVENT(bufferevent_socket_connect);
			return;
		}

		m_reactor.Start();
	}

	~SocketConnectorImp()
	{
		MYLIB_ASSERT_NOTNULL(m_client);
		m_client->Close();
		m_reactor.Stop(true);
		// delete bufferevent before deleting m_reactor.evbase to avoid memory leak.
		m_client->Release();
		m_client = NULL;
#if (COMM_FEATURE_SSL==1)
		SSL_CTX_free(server_ctx);
		server_ctx = NULL;
#endif
	}
};

#define SOCKETCONNECTOR ((SocketConnectorImp*)m_inst)
SocketConnector::SocketConnector(const SocketAddress& serverAddress, SocketClient* client):m_inst(NULL)
{
	m_inst = new SocketConnectorImp(serverAddress,client);
	MYLIB_ASSERT_NOTNULL(m_inst);
}
SocketConnector::~SocketConnector()
{
	if(NULL != m_inst)
	{
		delete SOCKETCONNECTOR;
		m_inst = NULL;
	}	
}
SocketClient* SocketConnector::get() const
{
	return SOCKETCONNECTOR->m_client;
}


////////////////////////////////////////////////////////////////////////// Event

static void __conn_readcb(struct bufferevent *bev, void* ctx)
{
	SocketClient* client = (SocketClient*)ctx;
	MYLIB_ASSERT_NOTNULL(client);
	struct evbuffer *input = bufferevent_get_input(bev);
	Buffer data;
	int nbytes;
	while ((nbytes = evbuffer_get_length(input)) > 0) 
	{
		data.resize(nbytes+1);
		evbuffer_remove(input, (char*)data, nbytes); 
		client->OnRead(data,nbytes);
	}
}

static void __close_event_cb(evutil_socket_t fd, short what, void *arg)
{
	SocketClient* client = (SocketClient*)arg;
	/*LOGNOTICE("Got an event on socket %d:%s%s%s%s!",
		(int) fd,
		(what&EV_TIMEOUT) ? " timeout" : "",
		(what&EV_READ)    ? " read" : "",
		(what&EV_WRITE)   ? " write" : "",
		(what&EV_SIGNAL)  ? " signal" : ""
		);*/

	if(what&EV_TIMEOUT)
	{
		CLIENTIMP(client)->StateChanged(SocketClient::CONN_CLOSE);
		if(!CLIENTIMP(client)->isConnector)
			client->Release();
	}
}

static void __conn_eventcb(struct bufferevent *bev, short events, void* ctx)
{
	SocketClient* client = (SocketClient*)ctx;

	if (events & BEV_EVENT_CONNECTED)
	{
		if(CLIENTIMP(client)->isConnector)
		{
			LOGNOTICE("Connected to server \"%s\".",(char*)client->Address().ToString());
			CLIENTIMP(client)->StateChanged(SocketClient::CONN_OPEN);
		}
		else
		{
			// SSL negotiation from server: bufferevent_openssl_socket_new(BUFFEREVENT_SSL_ACCEPTING);
			LOGNOTICE("SSL negotiation success to client \"%s\".",(char*)client->Address().ToString());
			CLIENTIMP(client)->StateChanged(SocketClient::CONN_OPEN);
		}
	}
	else if (events & BEV_EVENT_EOF) 
	{
		LOGNOTICE("Connection \"%s\" closed.",(char*)client->Address().ToString());
		CLIENTIMP(client)->StateChanged(SocketClient::CONN_EOF);
	} 
	else if (events & BEV_EVENT_ERROR) 
	{
		int err = EVUTIL_SOCKET_ERROR();
		if(0 == err /*&& CLIENTIMP(client)->isConnector*/)
		{
			LOGNOTICE("Connect to \"%s\" failure.",(char*)client->Address().ToString());
		}
		else
		{
			LOGNOTICE("Connection \"%s\" error: (%d) %s.",(char*)client->Address().ToString(),
				err,evutil_socket_error_to_string(err));
		}
		CLIENTIMP(client)->StateChanged(SocketClient::CONN_ERR);
	}
	else
	{
		LOGWARN("Un-catched connection events.");
	}
	/* None of the other events can happen here, since we haven't enabled
	 * timeouts */
	
}

static void __accept_conn_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *sa, int socklen, void* ctx)
{
	SocketAcceptorImp* acceptor = (SocketAcceptorImp*)ctx;
	SocketAddress clientAddress(sa,socklen);
#ifdef __GNUC__
	if(sa->sa_family == AF_UNIX)
		clientAddress = (const char*)String::format("%s:%d",(char*)acceptor->m_listenAddress.ToString(),acceptor->m_sockid++);
#endif
	LOGNOTICE("Connection \"%s\" established.",(char*)clientAddress.ToString());

	struct event_base* base = NULL;
	struct bufferevent* bev = NULL;
	base = evconnlistener_get_base(listener);
#if (COMM_FEATURE_SSL==1)
	LOGNOTICE("SSL negotiation to \"%s\" ...",(char*)clientAddress.ToString());
	SSL* client_ctx = SSL_new(acceptor->server_ctx);
	MYLIB_ASSERT_NOTNULL(client_ctx);
	bev = bufferevent_openssl_socket_new(base,fd,client_ctx,BUFFEREVENT_SSL_ACCEPTING,
		BEV_OPT_CLOSE_ON_FREE|BEV_OPT_THREADSAFE);
	if(NULL != bev)
		bufferevent_openssl_set_allow_dirty_shutdown(bev,1);
#else
	bev = bufferevent_socket_new(base,fd,BEV_OPT_CLOSE_ON_FREE|BEV_OPT_THREADSAFE);
#endif
	if(NULL == bev)
	{
		LOGFUNCERROR_EVENT(bufferevent_socket_new);
		return;
	}

	SocketClient* client = acceptor->m_factory->Create();
	{
		MYLIB_ASSERT_NOTNULL(client);
		CLIENTIMP(client)->address = clientAddress;
		CLIENTIMP(client)->bev = bev;
		bufferevent_setcb(bev, __conn_readcb, NULL, __conn_eventcb, client);
#if (COMM_FEATURE_SSL!=1)
		CLIENTIMP(client)->StateChanged(SocketClient::CONN_OPEN);
#endif
	}
}

static void __accept_error_cb(struct evconnlistener *listener, void* ctx)
{
	struct event_base* base = evconnlistener_get_base(listener);
	int err = EVUTIL_SOCKET_ERROR();
	LOGWARN("Got an error %d (%s) on the listener. Shutting down.",
		err,evutil_socket_error_to_string(err));
	event_base_loopbreak(base);
}


#endif
